// @Author EthanScriptOn
// @Desc
package factor

import (
	"context"
	"fmt"
	"gitee.com/fatzeng/srf_switch/core/switch_subject"
	"gitee.com/fatzeng/srf_switch_basic_components/util"
	"reflect"
	"time"
)

var DefaultFactorActuatorRegistry switch_subject.FactorActuatorRegistry = make(map[interface{}]switch_subject.FactorActuator)

func init() {
	DefaultFactorActuatorRegistry.Add(IP_FactorActuator,
		UserName_FactorActuator,
		Location_FactorActuator,
		UserId_FactorActuator,
		Time_FactorActuator,
		Single_FactorActuator,
		TelNum_FactorActuator,
		UserNick_FactorActuator,
		Custom_Arbitrarily_FactorActuator,
		Custom_Whole_FactorActuator)
}

var IP_FactorActuator = func(ctx context.Context, ruleRemark interface{}) (bool, error) {
	value := ctx.Value("ip")
	if value == nil {
		return false, nil
	}
	return sliceStringCheck(value, ruleRemark)
}
var UserName_FactorActuator = func(ctx context.Context, ruleRemark interface{}) (bool, error) {
	value := ctx.Value("user_name")
	if value == nil {
		return false, nil
	}
	return sliceStringCheck(value, ruleRemark)
}
var Location_FactorActuator = func(ctx context.Context, ruleRemark interface{}) (bool, error) {
	value := ctx.Value("location")
	if value == nil {
		return false, nil
	}
	return sliceStringCheck(value, ruleRemark)
}
var UserId_FactorActuator = func(ctx context.Context, ruleRemark interface{}) (bool, error) {
	value := ctx.Value("user_id")
	if value == nil {
		return false, nil
	}
	return sliceStringCheck(value, ruleRemark)
}
var Time_FactorActuator = func(ctx context.Context, ruleRemark interface{}) (bool, error) {
	rrValue := reflect.ValueOf(ruleRemark)
	if rrValue.Kind() == reflect.Ptr && !rrValue.IsNil() {
		rrValue = rrValue.Elem()
	}
	if rrValue.Kind() != reflect.Map {
		return false, fmt.Errorf("type error: [%+v] non map", ruleRemark)
	}
	getMap := make(map[string]int64)
	for _, keyValue := range rrValue.MapKeys() {
		valValue := rrValue.MapIndex(keyValue)
		str, ok := keyValue.Interface().(string)
		if !ok {
			return false, fmt.Errorf("type error: [%+v] non string", keyValue.Interface())
		}
		iVal, ok := valValue.Interface().(int64)
		if !ok {
			return false, fmt.Errorf("type error: [%+v] non int64", valValue.Interface())
		}
		getMap[str] = iVal
	}
	now := time.Now()
	beginUnixMilli, ok := getMap["begin"]
	if ok {
		if !time.UnixMilli(beginUnixMilli).Before(now) {
			return false, nil
		}
	}
	endUnixMilli, ok := getMap["end"]
	if ok {
		if !time.UnixMilli(endUnixMilli).After(now) {
			return false, nil
		}
	}
	return true, nil
}
var Single_FactorActuator = func(ctx context.Context, ruleRemark interface{}) (bool, error) {
	return isTypeOfString("on", ruleRemark)
}
var TelNum_FactorActuator = func(ctx context.Context, ruleRemark interface{}) (bool, error) {
	value := ctx.Value("tel_num")
	if value == nil {
		return false, nil
	}
	return sliceStringCheck(value, ruleRemark)
}
var UserNick_FactorActuator = func(ctx context.Context, ruleRemark interface{}) (bool, error) {
	value := ctx.Value("user_nick")
	if value == nil {
		return false, nil
	}
	return sliceStringCheck(value, ruleRemark)
}
var Custom_Arbitrarily_FactorActuator = func(ctx context.Context, ruleRemark interface{}) (bool, error) {
	customValue := ctx.Value("custom_map")
	ruleRemarkMap, err := util.ConvertMap(ruleRemark)
	if err != nil {
		return false, err
	}
	customMap, err := util.ConvertMap(customValue)
	if err != nil {
		return false, err
	}
	mCustomMap := customMap.(map[interface{}]interface{})
	mRuleRemarkMap := ruleRemarkMap.(map[interface{}]interface{})
	for key, val := range mRuleRemarkMap {
		customVal := mCustomMap[key]
		if customVal == val {
			return true, nil
		}
	}
	return true, nil
}
var Custom_Whole_FactorActuator = func(ctx context.Context, ruleRemark interface{}) (bool, error) {
	customValue := ctx.Value("custom_map")
	ruleRemarkMap, err := util.ConvertMap(ruleRemark)
	if err != nil {
		return false, err
	}
	customMap, err := util.ConvertMap(customValue)
	if err != nil {
		return false, err
	}
	mCustomMap := customMap.(map[interface{}]interface{})
	mRuleRemarkMap := ruleRemarkMap.(map[interface{}]interface{})
	for key, val := range mRuleRemarkMap {
		customVal := mCustomMap[key]
		if customVal != val {
			return false, nil
		}
	}
	return true, nil
}
var sliceStringCheck = func(checkValue interface{}, ruleRemark interface{}) (bool, error) {
	ofString, err := isTypeOfString(checkValue, ruleRemark)
	if err == nil {
		return ofString, nil
	}
	ofSlice, err := isTypeOfSlice(checkValue, ruleRemark)
	if err == nil {
		return ofSlice, nil
	}
	return false, fmt.Errorf("can only accept input parameters of string or slice type")
}
var isTypeOfString = func(checkValue interface{}, ruleRemark interface{}) (bool, error) {
	rrValue := reflect.ValueOf(ruleRemark)
	if rrValue.Kind() == reflect.Ptr && !rrValue.IsNil() {
		rrValue = rrValue.Elem()
	}
	switch rrValue.Kind() {
	case reflect.String:
		return reflect.DeepEqual(rrValue, checkValue), nil
	default:
		return false, fmt.Errorf("can only accept input parameters of string type")
	}
}
var isTypeOfSlice = func(checkValue interface{}, ruleRemark interface{}) (bool, error) {
	rrValue := reflect.ValueOf(ruleRemark)
	if rrValue.Kind() == reflect.Ptr && !rrValue.IsNil() {
		rrValue = rrValue.Elem()
	}
	switch rrValue.Kind() {
	case reflect.Slice:
		length := rrValue.Len()
		for i := 0; i < length; i++ {
			interVal := rrValue.Index(i).Interface()
			if reflect.DeepEqual(interVal, checkValue) {
				return true, nil
			}
		}
		return false, nil
	default:
		return false, fmt.Errorf("can only accept input parameters of array type")
	}
}
